#!/usr/bin/env python
# -*- coding: utf-8 -*-
# File: ck.py
"""
Arquitectura de referencia en commonKads. Modelo de la Aplicación.
Este módulo contiene todos los objetos del modelo de aplicación
en la arquitectura MVC (Modelo Vistas Controlador) de la arquitectura
de referencia de CommonKads.
  
"""
__author__ = "Arenas y Jurado"
__date__ = "30 de Agosto 2010"
__version__ = "$Revision: 0.2 - Basado en la version anterior de arhiju $"
__credits__ = """Universidad de Cordoba"""  
#import plncofelu
import codecs
from rdflib import Graph
from lxml import etree 
from nltk.corpus.reader import CategorizedPlaintextCorpusReader
import nltk
from nltk.tokenize import RegexpTokenizer
from tkFileDialog import asksaveasfilename,askopenfilename


#=================================================================================================
#              GESTIÓN DE ONTOLOGÍAS
#=================================================================================================

prefijo= '''
        PREFIX owl: <http://www.w3.org/2002/07/owl##>
        PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
        PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#/>
        PREFIX articulo: <http://www.owl-ontologies.com/Ontology1272364045.owl#>
        '''        


def todo():
    print 'Todas las subclases de la clase Persona '
     
    test_query = """
                SELECT   ?s ?p ?o
        WHERE { ?s ?p ?o}
     """ 
    test_query = prefijo + test_query
    store=Graph()
    store.parse('../articulos.owl')
    results = store.query(test_query)
    root = etree.fromstring(results.serialize(format='xml'))
    print(etree.tostring(root, pretty_print=True))


def getBodies(source='../articulos.owl',tr=0):
    if tr==True:
        print 'Todas los cuerpos de los textos:'
    store=Graph()
    store.parse('../articulos.owl')
    test_query = """
                SELECT   ?s  ?o
        WHERE { ?s articulo:Texto ?o}
     """ 
    test_query = prefijo + test_query
    
    results = store.query(test_query)
    root=etree.fromstring(results.serialize(format='xml'))
    if tr==True:
        print(etree.tostring(root, pretty_print=True))
    #Obtenemos todos los resultados de la consulta 
    results = root.xpath("//sparql:result",
               namespaces={'sparql': 'http://www.w3.org/2005/sparql-results#',
                           'articulo': 'http://www.owl-ontologies.com/Ontology1272364045.owl#'})
 
    cuerpos=[]#Lista de tuplas con los cuerpos de los correos.
    if tr==1:
        print results
    for result in  results:#Para cada resultado
        #Obtenemos el sujeto
        s=result.xpath("sparql:binding[@name='s']/sparql:uri",
               namespaces={'sparql': 'http://www.w3.org/2005/sparql-results#',
                           'articulo': 'http://www.owl-ontologies.com/Ontology1272364045.owl#'})
        print 's:',s,s[0].text
        #Obtenemos el objeto
        o=result.xpath("sparql:binding[@name='o']/sparql:literal",
               namespaces={'sparql': 'http://www.w3.org/2005/sparql-results#',
                           'articulo': 'http://www.owl-ontologies.com/Ontology1272364045.owl#'})
        if tr==True:
            print 'o:',o,o[0].text
        #Creamos una tupla de dos elementos
        t=(s[0].text.split('#')[1],o[0].text)
        cuerpos.append(t)
    return cuerpos

def getSubjects(source='../articulos.owl',tr=1):
    store=Graph()
    print 'Todas los cuerpos de los textos:'
    store.parse('../articulos.owl')
    test_query = """
                SELECT   ?s  
        WHERE { ?s articulo:Texto ?o}
     """ 
    test_query = prefijo + test_query
    
    results = store.query(test_query)
    root=etree.fromstring(results.serialize(format='xml'))
    if tr==True:
        print(etree.tostring(root, pretty_print=True))
    #Obtenemos todos los resultados de la consulta 
    results = root.xpath("//sparql:result",
               namespaces={'sparql': 'http://www.w3.org/2005/sparql-results#',
                           'articulo': 'http://www.owl-ontologies.com/Ontology1272364045.owl#'})
 
    subjects=[]#Lista de tuplas con los cuerpos de los correos.
    if tr==1:
        print results
    for result in  results:#Para cada resultado
        #Obtenemos el sujeto
        s=result.xpath("sparql:binding[@name='s']/sparql:uri",
               namespaces={'sparql': 'http://www.w3.org/2005/sparql-results#',
                           'articulo': 'http://www.owl-ontologies.com/Ontology1272364045.owl#'})
        if tr==True:
            print 's:',s,s[0].text
        subjects.append(s[0].text.split('#')[1])
                      
    return subjects

def getBody(source='../articulos.owl',subject='Articulo1',tr=True):
    if tr==True:
        print 'Estamos en getBody'
        print
        print source
    store=Graph()
    store.parse(source)
    test_query = ' SELECT   ?o   WHERE { articulo:'+subject+' articulo:Texto ?o}'
     
    test_query = prefijo + test_query
    
    results = store.query(test_query)
    root=etree.fromstring(results.serialize(format='xml'))
    if tr==True:
        print(etree.tostring(root, pretty_print=True))
    #Obtenemos todos los resultados de la consulta 
    results = root.xpath("//sparql:result",
               namespaces={'sparql': 'http://www.w3.org/2005/sparql-results#',
                           'articulo': 'http://www.owl-ontologies.com/Ontology1272364045.owl#'})
    bodies=[]
    for result in  results:#Para cada resultado
        #Obtenemos el objeto
        s=result.xpath("sparql:binding[@name='o']/sparql:literal",
               namespaces={'sparql': 'http://www.w3.org/2005/sparql-results#',
                           'articulo': 'http://www.owl-ontologies.com/Ontology1272364045.owl#'})
        
        if tr==True:
            print 's:',s,s[0].text
        bodies.append(s[0].text)
                      
    return bodies[0]
    
#===========================================================================================
#                     GESTIÓN DE CORPUS
#===========================================================================================
#Creación de un corpus de documentos categorizados temáticamente
parrafos_txt=CategorizedPlaintextCorpusReader('../../corpus/texto',
                                               '.*\.txt', cat_pattern=r'(ciencias|letras|sociales)/.*')
#============================================================================================
#                         INFERENCIAS
#============================================================================================


def document_features(document_words,word_features,tr=0):
    '''Dado el conjunto de palabras de un documento, obtiene las propiedades asociadas
       a ese documento. La propiedad a usar es si la palabra del conjunto de palabras
       se encuentra en el documento.
       @param document_words:  texto del documento en palabras o tokens.
       @param word_features: Lista de palabras que se usarán para obtener las propiedades
       @param tr: Traza
       @return: Devuelve un dicccionario, donde cada palabra del diccionario
                representa la propiedad y su valor.
                            
    '''
    features = {} #Diccionario de propiedades.
    for word in word_features:#Para cada característica (palabra) en la lista de características (palabras)
        features['contains(%s)' % word] = (word.lower() in document_words)#Obtine el valor de la característica
    if tr==True:
        print features
    #Devuelve las características del documento
    return features

def getCategories(corpusName=parrafos_txt,tr=0):
    '''Obtiene las categorías de un corpus
       @param corpusName: Instancia del corpus
       @param tr: Traza
       @return: Lista de las categorías del corpus
    '''
    if tr==True:
        print corpusName.categories()
        for c in corpusName.categories():
            print type(c)
    return  corpusName.categories()      

def getDocsFromCategories(corpusName=parrafos_txt, categories=['ciencias'],tr=0):
    '''Obtiene los documentos de una lista de categorías de un corpus.
       @param corpusName: Instancia del corpus.
       @param categories: Lista de categorías del corpus.
       @param tr: Traza.
       @return: Lista de documentos de esas categorías
    '''

    if tr==True:
        print corpusName.fileids(categories=categories)
        for d in corpusName.fileids(categories=categories):
            print type(d)
    return  corpusName.fileids(categories=categories)      

def getTextFromDoc(corpusName=parrafos_txt, fileids='letras/platon.txt',tr=0):
    '''Obtiene el texto  de un documento de un corpus.
       @param corpusName: Instancia del corpus.
       @param fileids: Identificación del documento.
       @param tr: Traza.
       @return: Texto del documento
    '''
    
    text=corpusName.raw(fileids=fileids)
    if tr==True:
        print text
    return text
    
def getDocumentsLabeled(corpusName=parrafos_txt,tr=0):
    '''Obtiene una lista de las palabras de cada documento de los documentos de un corpus categorizado
       con su categoría asignada.
       @param corpusName:  Nombre del corpus categorizado.
       @param tr: Traza
       @return: Devuelve una lista de tuplas, donde cada tupla
                representa una lista de las palabras de un documento y su categoría.
                            
    '''
    documents = [(list(corpusName.words(fileid)), category)
              for category in corpusName.categories()
              for fileid in corpusName.fileids(category)]
    return documents

def getFeaturesSets(word_features,word_documents_categorized,tr):
    '''Obtiene el conjunto de propiedades de un conjunto de documentos junto
       con su categoría asignada.
       @param word_features:  Palabras que se usarán de propiedades o características
       @param word_documents_categorized: Palabras de cada documento junto con su categoría
       @param tr: Traza
       @return: Devuelve una lista de tuplas. Cada tupla contiene por un lado una lista
                de propiedades y sus valores correspondientes y por otro
                la categoría de esa lista de palabras. 
                            
    '''

    featuresets = [(document_features(d,word_features), c) for (d,c) in word_documents_categorized]
    return featuresets

def getFeaturesSetsFromCategorizedCorpus(corpusName='parrafos_txt',nWords=10,tr=0):
    '''Obtiene un conjunto de propiedades de los documentos de un corpus de texto
       categorizado para construir un clasificador
       @param corpusName: Instancia del corpus
       @param nWords: Número de palabras más frecuentes a considerar 
       @param tr: Traza
       @return featuresets: matriz de características
    '''
    word_documents_categorized= getDocumentsLabeled(corpusName=corpusName,tr=0)
    #print documentos[0]
    #Obtiene las palabras más frecuentes que se usarán para clasificar el texto
    word_features =getWordFeatures(corpusName=parrafos_txt,nWords=nWords,tr=0)
    #Obtiene el conjunto de características y su categoría asociadada a un conjunto de documentos
    featuresets= getFeaturesSets(word_features=word_features,
                                 word_documents_categorized=word_documents_categorized,tr=tr)
    return featuresets
def getWordFeatures(corpusName,nWords=10,tr=0):
    '''Dado un corpus obtiene las  nWords palabras más frecuentes de ese Corpus que se usarán como características.
       @param nameCorpus:  Nombre del corpus.
       @param nWords: Número máximo de palabras que se usarán para obtener propiedades.
       @param tr: Traza
       @return: Devuelve una lista de tuplas, donde cada tupla
                representa la propiedad y su valor.
                            
    '''
    all_words = nltk.FreqDist(w for w in corpusName.words())
    #Obtiene las 200 primeras palabras
    #=================================
    if nWords>len(all_words):
        nWords=len(all_words)
        print nWords
    word_features = all_words.keys()[:nWords]
    if tr==True:
        print
        print 'Imprime todas las palabras'
        print '=========================='
        print word_features
        print len(set(word_features))
        word_features = set(word_features)
        for i in word_features:
            #print i
            pass
    
    irrelevantes = ['de','el','los','la','lo','las','del','a','un','una','unos','unas','.',',','y','que','es','en','con','no','si','al','se','me','o','su','(',')','como','por',':',';','"','s','sica','pero','El','ha','),',').','-','entre','desde','para',' ','Con','En','La','La','Los','Las','Lo','dicas','\xad','\xa1','m\xc3','\xa9','da','son','tanto','De']
    #irrelevantes = []
    
    #Eliminamos palabras NO relevantes
    repetir = True
    while repetir == True:
		repetir = False
		for word in word_features:
			for i in irrelevantes:
				if repetir == False:
					if word == i:
						word_features.remove(i)
						repetir = True
						
    
    return word_features

def paragraph_features(paragraph):
    '''Obtiene las propiedades de un párrafo, indicando si una palabra está
       en el mismo.
'''
    tokenizer = RegexpTokenizer('\w+|\$[\d\.]+|\S+')
    words = tokenizer.tokenize(paragraph)
    paragraph_words = set(words)
    features = {}
    for word in paragraph_words:
        features['contains(%s)' % word] = (word in paragraph_words)
    return features

def paragraph_features_w(paragraph,word_features,tr):
    '''Obtiene las propiedades de un párrafo, indicando si una palabra está
       en el mismo.
'''
    tokenizer = RegexpTokenizer('\w+|\$[\d\.]+|\S+')
    words = tokenizer.tokenize(paragraph)
    paragraph_words = set(words)
    features = {}
    for word in word_features:
        features['contains(%s)' % word] = (word in paragraph_words)
    return features
def classifyCat(corpusName=parrafos_txt,nWords=100,modelClassify='',percentTrain=80,tr=True):
    '''Obsoleta'''
    
    #Clasificación desde corpus etiquetados
    #======================================
    #print parrafos_txt#Corpus de párrafos etiquetados
    #print parrafos_txt.readme()
    #print parrafos_txt.categories()
    print '=================================================================================================='
    #print parrafos_txt.fileids()
    for f in parrafos_txt.fileids():
        print f, parrafos_txt.words(f)
        pass
    
    #Construcción de un clasificador tomando las nWords más frecuentes como propiedades incluidas
    #=============================================================================================
    #1.- Obtención de la matriz de características de los documentos 
    featuresets=getFeaturesSetsFromCategorizedCorpus(corpusName=corpusName,nWords=nWords,tr=tr)
    #2.- Definción del conjunto de entrenamiento y conjunto test
    nTrain=int((float(percentTrain)/100)*len(featuresets))
    print 'nTrn=',int(nTrain), percentTrain, len(featuresets)
    nTest= len(featuresets)-nTrain
    print 'nTest=',nTest
    
    train_set, test_set = featuresets[0:nTrain], featuresets[nTrain:]
    print 'Conjuntos:', len(train_set), len(test_set)
    #3.- Obtención del clasificador
    if modelClassify=='Clasificador Bayesiano':
        classifier = nltk.NaiveBayesClassifier.train(train_set)
    elif modelClassify=='Árboles de decisión':
        classifier = nltk.DecisionTreeClassifier.train(train_set)
    #4.- Precisión del clasificador
    print 'Precisón: ',  nltk.classify.accuracy(classifier, test_set)
    
    #Obtiene las propiedades de un documento a partir de las palabras que se usarán
    #como propiedades
    word_features = getWordFeatures(corpusName=corpusName,nWords=nWords,tr=tr)
    #5.- Definición de unas palabras que sirvan como propiedades
    word_features=['hermano','alcalde','reza','Virgen','Dios','padre','hijo']
    #6.- Obtención de las propiedades de un documento a partir del texto y de las palabras clave
    p=document_features(parrafos_txt.words('ciencias/green.txt'),word_features)
    print 'Propiedades de un documento:'
    print '============================'
    for i in p:
        #print i, p[i]
        pass
    print 'Obtiene el conjunto de características en cada documento y su categoría asociadada a un conjunto de documentos:'
    print '============================================================================================='

    for d in featuresets:
        #print d[0],'-->', d[1]
        #print '000'
        pass

    p=document_features(parrafos_txt.words('letras/becquer.txt'),word_features)
    p=document_features("José reza a la Virgen María".split(),word_features)
    #print p
    print classifier.classify(p)
    return classifier
#=================================================================================================================
#                                     TAREAS
#=================================================================================================================
def trainClassifier_Task(corpusName=parrafos_txt,nWords=100,modelClassify='Clasificador Bayesiano',percentTrain=80,tr=True):
   
    #Construcción de un clasificador tomando las nWords más frecuentes como propiedades incluidas
    #=============================================================================================
    #1.- Obtención de la matriz de características de los documentos 
    featuresets=getFeaturesSetsFromCategorizedCorpus(corpusName=corpusName,nWords=nWords,tr=tr)
    #2.- Definción del conjunto de entrenamiento y conjunto test
    nTrain=int((float(percentTrain)/100)*len(featuresets))
    #print 'nTrn=',int(nTrain), percentTrain, len(featuresets)
    nTest= len(featuresets)-nTrain
    #print 'nTest=',nTest
    train_set, test_set = featuresets[0:nTrain], featuresets[nTrain:]
    if tr==True:
        print 'Conjuntos Entrenamiento, test:', len(train_set), len(test_set)
    #3.- Obtención del clasificador
    if modelClassify=='Clasificador Bayesiano':
        classifier = nltk.NaiveBayesClassifier.train(train_set)
    elif modelClassify=='Árboles de decisión':
        classifier = nltk.DecisionTreeClassifier.train(train_set)
    #4.- Precisión del clasificador
    accuracy=nltk.classify.accuracy(classifier, test_set)
    if tr==True:
        print 'Precisón: ',  accuracy 
    
    return (classifier,accuracy)



# IMPLEMENTACIÓN METIDO DE LA PODA

def MetodoPoda(text_words, categorias, listaPalabras):
	cat_aux = []
		
	#recorre la lista de palabras del texto y elimina las categorias NO posibles
	for palabra in text_words:
		#Elimino puntos o comas al final
		if palabra[len(palabra)-1] == '.':
			palabra = palabra[:len(palabra)-1]
		elif palabra[len(palabra)-1] == '.':
			palabra = palabra[:len(palabra)-1]
		#Ejecuto si quedan categorias en la lista
		if len(categorias) > 1:
			#crea una lista de categorias posibles
			for i in listaPalabras:
				print "=> INICIO!! i:", i, "palabra:", palabra
				if i[0] == palabra:
					cat_aux.append(i[1])
					print "Palabra encontrada: ", palabra
					print "CAT_AUX AÑADIDA ->", i[1]
			#elimina categorias NO posibles
			if len(cat_aux) != 0:
				repetir = 1
				while repetir == 1:
					repetir = 0
					for j in categorias:
						conservar = False
						if repetir == 0:
							for k in cat_aux:
								print "##CONDICION j=", j, "//", "k=", k
								if j == k:
									conservar = True
									print "Conservar j=k=",j
							if conservar == False:
								categorias.remove(j)
								repetir = 1
								print "***ELIMINADA: ", j
				cat_aux = []
			print categorias
	
	return categorias

def getPalabras(nom_fich):
	'''Obtiene la lista de palabras categorizadas de un fichero dado en txt
	'''
	listaPalabras = []
	fich = open(nom_fich, "r")
	
	while True:
		linea = fich.readline()
		if not linea:
			break
		palabra = linea.split()
		listaPalabras.append((palabra[0], palabra[1]))
		
	
	fich.close()
	return listaPalabras

def AbrirArchivo():#Funcion para abrir un archivo
	fileName = askopenfilename()
	try:
		file = open(fileName, 'r')
		texto = file.read()

		#self.text.delete(0.0, END)
		#self.text.insert(0.0, contents)
		return texto
	except:
		return False


def AbrirArchivo(fileName):#Funcion para abrir un archivo pasando el nombre como parametro
	
	try:
		file = open(fileName, 'r')
		texto = file.read()

		#self.text.delete(0.0, END)
		#self.text.insert(0.0, contents)
		return texto
	except:
		return False

if __name__ == "__main__":
    #c=getSubjects(source='../articulos.owl',tr=False)
    #c=getBody(source='../articulos.owl',subject='Correo_1',tr=False)
    #print parrafos_txt.fileids()
    #print parrafos_txt.categories()
    if True:
        cl=trainClassifier_Task(corpusName=parrafos_txt,nWords=100,
                                modelClassify='Clasificador Bayesiano',percentTrain=50,tr=0)
        print cl, cl[0], cl[1]
    if False:
        
        word_features=['hermano','alcalde','reza','Virgen','Dios','padre','hijo']
        p=document_features("José reza a la Virgen María".split(),word_features)
        print
        print type(p), len(p), p.keys(), 
        print
        print p
        print
        for i in p.keys():
            print i + ':' +str(p[i])
        print cl.classify(p)
        getCategories(corpusName=parrafos_txt,tr=1)
        getDocsFromCategories(corpusName=parrafos_txt, categories=['letras'],tr=1)
        
        getTextFromDoc(corpusName=parrafos_txt,  fileids='letras/platon.txt',tr=0)
        print paragraph_features_w(paragraph='la casa',word_features=['la'],tr=1)
     



